//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "BindingInfo.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHTEXT 500000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace ExclusionLigand {
  
  bool EqualLigand (TNode <String>* ligand, void* ref) {

    return (!strcmp(ligand->Info()->Out(), ((String*)ref)->Out()))?true:false;

  }
  
  bool EqualLigandNumber (TNode <LigandDomainNumber>* ligand, void* ref) {
    
    LigandNearResidues* ligandRef = (LigandNearResidues*)ref;

    return ((!strcmp(ligand->Info()->name->Out(), ligandRef->LigandName()->Out())) && (ligand->Info()->number == ligandRef->LigandNumber()))?true:false;

  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingInfo::BindingInfo () {
  
  this->domain               = new String ();
  this->format               = new String ();
  this->cathCode             = new String ();
  this->segments             = new TListE <SegmentCDDF> ();   
  this->nearResiduesByLigand = new TListE <LigandNearResidues> ();
  this->toString             = new String ();
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingInfo::BindingInfo (const BindingInfo& bindingInfo) {
  
  this->domain               = new String (bindingInfo.domain);
  this->format               = new String (bindingInfo.format);
  this->cathCode             = new String (bindingInfo.cathCode);
  this->segments             = new TListE <SegmentCDDF> (bindingInfo.segments);   
  this->nearResiduesByLigand = new TListE <LigandNearResidues> (bindingInfo.nearResiduesByLigand);
  this->toString             = new String ();
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingInfo::BindingInfo (const BindingInfo* bindingInfo) {
  
  this->domain               = new String (bindingInfo->domain);
  this->format               = new String (bindingInfo->format);
  this->cathCode             = new String (bindingInfo->cathCode);
  this->segments             = new TListE <SegmentCDDF> (bindingInfo->segments);   
  this->nearResiduesByLigand = new TListE <LigandNearResidues> (bindingInfo->nearResiduesByLigand);
  this->toString             = new String ();
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingInfo::~BindingInfo () {
  
  if (this->domain)               delete this->domain;
  if (this->format)               delete this->format;
  if (this->cathCode)             delete this->cathCode;
  if (this->segments)             delete this->segments;   
  if (this->nearResiduesByLigand) delete this->nearResiduesByLigand;
  if (this->toString)             delete this->toString;
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingInfo::Domain (String* domain) {
  
  if (domain) this->domain->In(domain);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingInfo::Format (String* format) {
  
  if (format) this->format->In(format);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingInfo::CathCode (String* cathCode) {
  
  if (cathCode) this->cathCode->In(cathCode);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingInfo::Segments (TListE <SegmentCDDF>* segments) {
  
  if (segments) *(this->segments) = *(segments);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingInfo::NearResiduesByLigand (TListE <LigandNearResidues>* nearResiduesByLigand) {
  
  if (nearResiduesByLigand) *(this->nearResiduesByLigand) = *(nearResiduesByLigand);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::Domain (void) {
  
  return this->domain;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::Format (void) {
  
  return this->format;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::CathCode (void) {
  
  return this->cathCode;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <SegmentCDDF>* BindingInfo::Segments (void) {
  
  return this->segments;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <LigandNearResidues>* BindingInfo::NearResiduesByLigand (void) {
  
  return this->nearResiduesByLigand;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingInfo::ReadFromString (String* data) {
  
  if (!data || !data->Length()) return false;

  this->CleanData();

  char   tmpData [data->Length() + 1];
  char*  line = NULL;
  char*  parts = NULL;
  String header;
  
  strcpy(tmpData, data->Out());
  tmpData[data->Length()] = '\0';

  if (!(line = strtok(tmpData, "\n"))) return false;
  
  header.In(line + 1);
  header.Trim();
  
  if (header.Length()) {
    
    strcpy(tmpData, header.Out());
    
    line = strtok(tmpData, " ");
    if (line) { this->domain->In(line); this->domain->RTrim(); }
    
    line = strtok(NULL, " ()");
    if (line) { this->format->In(line); this->format->RTrim(); }
    
    bool readSegment = false;
    
    line = strtok(NULL, " ");
    if (line) { 
      
      if (line[0] == '-') { 
        
        line = strtok(NULL, " ");
        
        this->cathCode->In(line); 
        this->cathCode->RTrim(); 
        
      }
      else if (line[0] == '=') {
        
        readSegment = true;
        
        line = strtok(NULL, " [;]");
        
        while (line) {
          
          SegmentCDDF* newSegment = new SegmentCDDF ();
          newSegment->StoreData(line);
          
          this->segments->Add(newSegment);
          
          line = strtok(NULL, " [;]");
          
        }
        
      }
      
    }
    
    if (!readSegment) {
      
      line = strtok(NULL, " ");
      
      if (line && line[0] == '=') {

        line = strtok(NULL, " [;]");

        while (line) {

          SegmentCDDF* newSegment = new SegmentCDDF ();
          newSegment->StoreData(line);

          this->segments->Add(newSegment);

          line = strtok(NULL, " [;]");

        }

      }
      
    }
    
    this->segments->SetInitial();
    while (this->segments->SetNext())
      this->segments->GetCurrent()->ReadPair();
    
  }

  strcpy(tmpData, data->Out());
  tmpData[data->Length()] = '\0';
  
  if (!(line = strtok(tmpData, "\n"))) return false;
  line = strtok(NULL, "\n");

  TListE <String>* ligandInfo = new TListE <String> ();
  while (line) {
    ligandInfo->Add(new String (line));    
    line = strtok(NULL, "\n");
  }
  
  ligandInfo->SetInitial();
  while (ligandInfo->SetNext()) {
    
    LigandNearResidues* newLigandNearResidues = new LigandNearResidues ();
    
    if (newLigandNearResidues->ReadFromString(ligandInfo->GetCurrent()))
      this->nearResiduesByLigand->Add(newLigandNearResidues);
    else
      delete newLigandNearResidues;
    
  }
  
  if (ligandInfo) delete ligandInfo;

  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingInfo::ReadFromString (char* data) {
  
  if (!data || !strlen(data)) return false;
  
  String dataChar;
  dataChar.In(data);
  
  return this->ReadFromString(&dataChar);    

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingInfo::DeleteResiduesAppearAsLigand (TListE <String>* exclusionList) {
  
  if (!this->domain   || !this->domain->Length()     || !exclusionList ||
      !this->segments || !this->nearResiduesByLigand)
    return false;
  
  if (!this->segments->Length()) 
    return false;
  
  this->nearResiduesByLigand->SetInitial();
  while (this->nearResiduesByLigand->SetNext()) {
    
    LigandNearResidues* currentLigandNearResidues = this->nearResiduesByLigand->GetCurrent();
    
    this->segments->SetInitial();
    while (this->segments->SetNext()) {
      
      SegmentCDDF* currentSegmentCDDF = this->segments->GetCurrent();
      
      if (currentLigandNearResidues->LigandNumber() >= currentSegmentCDDF->RangeStart() &&
          currentLigandNearResidues->LigandNumber() <= currentSegmentCDDF->RangeEnd()) {
        
        String* searchLigand = exclusionList->FirstOcurrence(ExclusionLigand::EqualLigand, currentLigandNearResidues->LigandName());
        
        if (!searchLigand) exclusionList->Add(new String (currentLigandNearResidues->LigandName()));
        
        if (!this->nearResiduesByLigand->GetPrevious()) {
          this->nearResiduesByLigand->DeleteCurrent();
          this->nearResiduesByLigand->SetInitial();
        }
        else {
          this->nearResiduesByLigand->DeleteCurrent();
          this->nearResiduesByLigand->SetPrevious();
        }
        
        break;
        
      }

    }
    
  }
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingInfo::DeleteLigandsAppearInList (TListE <String>* exclusionList) {
  
  if (!this->nearResiduesByLigand || !exclusionList)
    return false;
  
  this->nearResiduesByLigand->SetInitial();
  while (this->nearResiduesByLigand->SetNext()) {
    
    LigandNearResidues* currentLigandNearResidues = this->nearResiduesByLigand->GetCurrent();

    exclusionList->SetInitial();
    while (exclusionList->SetNext()) {
      
      if (!strcmp(currentLigandNearResidues->LigandName()->Out(), exclusionList->GetCurrent()->Out())) {
        
        if (!this->nearResiduesByLigand->GetPrevious()) {
          this->nearResiduesByLigand->DeleteCurrent();
          this->nearResiduesByLigand->SetInitial();
        }
        else {
          this->nearResiduesByLigand->DeleteCurrent();
          this->nearResiduesByLigand->SetPrevious();
        }
        
        break;
        
      }
      
    }
    
  }
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingInfo::HasAtLeastOneLigandInList (TListE <LigandDomainNumber>* ligandsList) {
  
  if (!this->nearResiduesByLigand || !ligandsList)
    return false;
  
  this->nearResiduesByLigand->SetInitial();
  while (this->nearResiduesByLigand->SetNext()) {
    
    ligandsList->SetInitial();
    while (ligandsList->SetNext()) {
     
     if ((!strcmp(this->nearResiduesByLigand->GetCurrent()->LigandName()->Out(), ligandsList->GetCurrent()->name->Out())) &&
         (this->nearResiduesByLigand->GetCurrent()->LigandNumber() == ligandsList->GetCurrent()->number)                  && 
         (ligandsList->GetCurrentNode()->IsVisible()))
       return true;
    }
    
  }
  
  return false;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingInfo::HasALigandName (LigandDomainNumber* ligand) {
  
  if (!this->nearResiduesByLigand || !ligand)
    return false;
  
  this->nearResiduesByLigand->SetInitial();
  while (this->nearResiduesByLigand->SetNext()) 
    if (!strcmp(this->nearResiduesByLigand->GetCurrent()->LigandName()->Out(), ligand->name->Out()))
      return true;
  
  return false;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::ToStringSegment (void) {
  
  this->toString->In("");
  
  char buffer [LENGTHTEXT + 1];
  bool first = true;
  
  buffer[0] = '\0';
  
  this->segments->SetInitial();
  while (this->segments->SetNext()) {
    
    SegmentCDDF* currentSegment = this->segments->GetCurrent();
    
    sprintf (buffer, (first)?"%s%d-%d\0":"%s;%d-%d\0",  buffer, currentSegment->RangeStart(), currentSegment->RangeEnd());
    
    first = false;
    
  }
  
  this->toString->In(buffer);
  
  return this->toString;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::ToString (void) {
  
  this->ToStringHeader();
  this->toString->Add("\n");
  
  this->nearResiduesByLigand->SetInitial();
  while (this->nearResiduesByLigand->SetNext()) {
    
    this->toString->Add(this->nearResiduesByLigand->GetCurrent()->ToString()->Out());
    this->toString->Add("\n");
    
  }
  
  return this->toString;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::ToStringForLigand (LigandDomainNumber* ligand) {
  
  this->toString->In("");
  
  if (!ligand) return this->toString;
  
  this->ToStringHeader();
  this->toString->Add("\n");
  
  this->nearResiduesByLigand->SetInitial();
  while (this->nearResiduesByLigand->SetNext()) {
    
    if (!strcmp(ligand->name->Out(), this->nearResiduesByLigand->GetCurrent()->LigandName()->Out())) {
      this->toString->Add(this->nearResiduesByLigand->GetCurrent()->ToString()->Out());
      this->toString->Add("\n");
    }
    
  }
  
  return this->toString;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::ToStringForLigands (TListE <LigandDomainNumber>* ligands) {
  
  this->toString->In("");
  
  if (!ligands) return this->toString;
  
  this->ToStringHeader();
  this->toString->Add("\n");
  
  this->nearResiduesByLigand->SetInitial();
  while (this->nearResiduesByLigand->SetNext()) {
    
    int positionLigand = ligands->FirstOcurrencePosition(ExclusionLigand::EqualLigandNumber, this->nearResiduesByLigand->GetCurrent());
    
    if ((positionLigand != -1) && (ligands->GetNode(positionLigand)->IsVisible())) {
      this->toString->Add(this->nearResiduesByLigand->GetCurrent()->ToString()->Out());
      this->toString->Add("\n");
    }
    
  }
  
  return this->toString;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingInfo::ToStringHeader (void) {
  
  char    buffer [LENGTHTEXT + 1];
  
  sprintf(buffer, ">%s (%s) - %s = [%s]\0", this->domain->Out(), 
                                            this->format->Out(), 
                                            this->cathCode->Out(), 
                                            this->ToStringSegment()->Out());
  
  this->toString->In(buffer);
  
  return this->toString;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingInfo::CleanData (void) {
  
  this->domain->In("");
  this->format->In("");
  this->cathCode->In("");
  this->segments->Clear();
  this->nearResiduesByLigand->Clear();
  this->toString->In("");
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingInfo BindingInfo::operator= (const BindingInfo& bindingInfo) {
  
  this->domain->In(bindingInfo.domain);
  this->format->In(bindingInfo.format);
  this->cathCode->In(bindingInfo.cathCode);

  *(this->segments)             = *(bindingInfo.segments);   
  *(this->nearResiduesByLigand) = *(bindingInfo.nearResiduesByLigand);
  
  return *this;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
